package com.javacoo.lock.client.aspect;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.javacoo.lock.client.annotation.MethodLock;
import com.javacoo.lock.client.api.Lock;

import lombok.extern.slf4j.Slf4j;

/**
 * 业务拦截器
 * <li></li>
 * @author duanyong
 * @date 2021/3/1 15:56
 */
@Slf4j
@Aspect
@Component
public class LockAspect {
    /**锁对象*/
    @Autowired
    private Lock lock;


    @Around("@annotation(methodLock)")
    public Object around(ProceedingJoinPoint joinPoint, MethodLock methodLock) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取被拦截的方法
        Method method = signature.getMethod();
        // 获取被拦截的类名
        String className = signature.getDeclaringType().getSimpleName();
        // 获取被拦截的方法名
        String methodName = method.getName();
        log.info("[加锁交易请求开始]，方法口名:{}", className+"."+methodName);
        //生成锁KEY
        String lockKey = generateKey(joinPoint.getArgs(),className,methodName,methodLock);
        if(tryLock(lockKey,methodLock)){
            try {
                log.info("[加锁交易]加锁成功,MethodName:{},key:{}",methodName,lockKey);
                return joinPoint.proceed();
            } catch (Exception e) {
                log.error("[加锁交易]执行方法发生异常,MethodName:{},lockKey:{},Exception:{}",methodName,lockKey,e);
                throw e;
            }finally {
                lock.unlock(lockKey);
                log.info("[加锁交易]方法解锁,MethodName:{},key:{}",methodName,lockKey);
            }
        }else{
            log.error("[加锁交易]过滤频繁操作,加锁失败,Key:{}",lockKey);
        }
        return null;
    }

    /**
     * 对添加RedisLock注解的方法进行重复访问限制
     * @param cacheKey
     * @param methodLock
     */
    private boolean tryLock(String cacheKey,MethodLock methodLock) {
        boolean isLocked = lock.tryLock(cacheKey, TimeUnit.SECONDS,0,methodLock.timeInSecond());
        if(isLocked){
            log.info("[交易系统拦截器]加锁成功,KEY:{}",cacheKey);
        }
        return isLocked;
    }
    /**
     * 生成锁的key key=类名-方法名-参数集合
     * <li></li>
     * @author duanyong
     * @date 2021/4/29 13:08
     * @param args: 参数
     * @param className: 类名
     * @param methodName: 方法名
     * @param methodLock: 锁接口
     * @return: java.lang.String 锁的key
     */
    private String generateKey(Object[] args, String className, String methodName,MethodLock methodLock) throws Exception {
        //根据参数列表索引获取入参 默认为第一个参数
        Object param = args[methodLock.paramIndex()];
        List<String> fieldValueList = new ArrayList<>();
        String[] fieldNames = methodLock.fieldName();
        if(param instanceof String){
            fieldValueList.add(String.valueOf(param));
        }else if(param instanceof Long){
            fieldValueList.add(String.valueOf(param));
        }else{
            String jsonString = JSON.toJSONString(param);
            JSONObject jsonObject = JSON.parseObject(jsonString);
            for (String filedName:fieldNames){
                if(jsonObject.containsKey(filedName)){
                    fieldValueList.add(jsonObject.getString(filedName));
                }
            }
        }

        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(className)
            .append("-")
            .append(methodName)
            .append("-")
            .append(fieldValueList);
        return keyBuilder.toString();
    }
}
